﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.ComponentModel;
using System.Data.SqlClient;

namespace SqlServerDriver
{
	//
	// Class to manage long standing connections and release them
	//
	internal class ConnectionMap
	{
		const int CHECK_FREQUENCY_SECONDS = 600; // 10 mins.
		// Singleton pattern
		static ConnectionMap theInstance = new ConnectionMap();
		Dictionary<String, DateTime> access = new Dictionary<string,DateTime>();
		Thread workerThread;

		ConnectionMap()
		{
		}

		public void StartCleanupMonitor()
		{
			// Implement IDispose in SqlDriver to kill this thread.
			if (this.workerThread != null)
				return;
			this.workerThread = new Thread(this.CheckConnectionMap);
			// Start the worker thread.
			this.workerThread.Start();
		}

		public void StopCleanupMonitor()
		{
			// Start the worker thread.
			if (this.workerThread != null)
				this.workerThread.Abort();
		}

		void CheckConnectionMap()
		{
			while (true)
			{
				DateTime cutOff = DateTime.Now;
				Thread.Sleep(CHECK_FREQUENCY_SECONDS * 1000);   // seconds -> ms.

				String[] stale = ConnectionMap.Instance.Remove(cutOff);
				foreach (String staleConnectionString in stale)
				{
					using (SqlConnection clearupSqlConn = new SqlConnection())
					{
						clearupSqlConn.ConnectionString = staleConnectionString;
						SqlConnection.ClearPool(clearupSqlConn);
					}
				}
			}
		}

		static internal ConnectionMap Instance
		{
			get { return theInstance; }
		}

		// Flag last time a connection was used. Add to the store if first use.
		internal void Update(String connectionString, DateTime lastAccess)
		{
			if (!this.access.ContainsKey(connectionString))
			{
				this.access.Add(connectionString, lastAccess);
			}
			else
			{
				this.access[connectionString] = lastAccess;
			}
		}
		
		// Remove all entries from the store not accessed after the date time sent in
		// return the connections for removal from the connection pool
		internal String[] Remove(DateTime beforeThis)
		{
			// first find
			List<String> removed = new List<string>();
			foreach (String connectionString in this.access.Keys)
			{
				if (this.access[connectionString] < beforeThis)
				{
					removed.Add(connectionString);
				}
			}
			// then remove. It would throw an error if you try it in one.
			foreach (String connectionString in removed)
			{
				this.access.Remove(connectionString);
			}
			return removed.ToArray();
		}
	}
	
	//
	// Class to manage long standing connections and release them
	//
	internal class HouseKeeper
	{
	}
}
